Skip to content

docs(creative): clarify cta errors, carousel defaults, asset_types and product formats - #8949

Open
Synaeve wants to merge 5 commits into
adcontextprotocol:mainfrom
Synaeve:docs/8628-creative-wording-answers
Open

Synaeve wants to merge 5 commits into
adcontextprotocol:mainfrom
Synaeve:docs/8628-creative-wording-answers

Conversation

@Synaeve

@Synaeve Synaeve commented Nov 12, 2026 •

Copy link
Copy Markdown
Contributor

Closes #8628
Closes #8629
Closes #8630
Closes #8631

Problem

An implementer building sync_creatives validation against v3.2.7 found four places where the creative schemas and docs read two ways. The maintainer answered each issue with the intended rule. This PR writes those answers into the schemas and docs. It builds on #8937 (card_image_sizes), #8940 and #8941 (format conformance), which merged just before.

Please confirm: schema defaults vs. #8941's omitted-parameter rule

#8941 says an omitted narrowing parameter sets no limit. Under #8629 a silent allowed_card_media_asset_types means ['image'] ("the parameter states its own default"), so the two texts disagreed. This PR adds one exception wherever #8941's rule is stated:

  • docs/creative/task-reference/sync_creatives.mdx
  • docs/creative/canonical-formats.mdx
  • docs/creative/formats.mdx

The exception: a parameter whose canonical schema states a default applies that default when omitted. The rule as written also contradicted defaults that already exist, such as every canonical's default slots and seller_rendered_stateful_display.clickthrough: "required". Among the canonical defaults, only allowed_card_media_asset_types changes what a seller may refuse. If you prefer, this can be narrowed to "a parameter that states its own default".

Changes

#8628 cta_values and CREATIVE_VALUE_NOT_ALLOWED

  • enums/error-code.json: the code covers a value outside the closed set the targeted option declares (cta_values on canonical formats, allowed_values in text-asset requirements). It turns on the declared set, not the container, so a card's per-card cta is covered.
  • The declared set goes in error.details.accepted_values, the canonical key in core/error.json; also sending allowed_values is harmless. The enumMetadata suggestion names accepted_values.
  • error.field examples are now creatives[0].assets.cta.content and creatives[0].assets.cards[2].cta. This drops the creative_manifest segment from the maintainer's example (creatives[0].creative_manifest.assets.cards[2].cta):
    • sync_creatives items (core/creative-asset.json) carry assets directly, and core/error.json defines field as a request-payload path.
    • The segment came from the old assets[0].value example.
    • The training agent already emits creatives[0].assets.cta.content.
  • core/requirements/text-asset-requirements.json (allowed_values): MUST reject and set error.field; SHOULD carry the list in error.details.accepted_values. This matches the enum, so a seller that sends the old allowed_values stays conformant. core/assets/card-asset.json (cta) names the same code.
  • sync_creatives.mdx: the format-conformance paragraph and the error table use accepted_values and mention the per-card cta.
  • canonical-formats.mdx Reels table: the buyer ships assets.cta.content, not assets.cta.text.
  • Regenerated docs/snippets/compliance-error-codes.mdx.
  • Training agent: CREATIVE_VALUE_NOT_ALLOWED details carry accepted_values instead of allowed_values. No storyboard reads the details key.

#8629 image_carousel card media and card count

  • allowed_card_media_asset_types gains "default": ["image"]. A declaration that omits both it and the deprecated allowed_card_asset_types alias accepts image cards only; declare ['image', 'video'] for mixed-media carousels.
  • The format description keeps the narrowing framing: media can carry image or video, and the parameter narrows that set and defaults to ['image'].
  • min_cards / max_cards only narrow the cards slot. max_cards: 35 alone still caps at 10; a product that takes 35 cards restates the slot with max: 35 and may also set max_cards: 35.
  • responsive_creative: pool counts narrow their slot the same way. card-asset.json media: image only when the parameter is omitted.
  • carousels.mdx gains a "Card media and card count" section with a 35-card example. canonical-formats.mdx (params.slots section, Pinterest Idea Pin row) says the same.
  • The schema-default exception above, in three files.

#8630 list_creative_formats asset_types

  • media-buy/ and creative/list-creative-formats-request.json: keep formats that declare every one of the requested types. An asset counts whether it is required or optional, and whether it is at the top level or inside a repeatable_group. The example matches.
  • migration/creatives.mdx states the all-of rule and that any-of means one call per type, unioned. list_creatives.mdx ends its matching paragraph with a contrast to its own any-of filter over creatives, which is unchanged.
  • Reference creative agent: list_creative_formats applies all-of (it applied any-of), and its tool description says so. No storyboard sends list_creative_formats with asset_types.

#8631 multi-publisher products without placements[]

  • core/product.json format_options: adopts the suggested wording. Such a product MUST NOT carry publisher-scoped declarations; when no product-local declaration remains, the seller MUST publish placements[] or split the product by publisher.
  • formats.mdx, canonical-formats.mdx and media-products.mdx state:
    • the rule;
    • that the intersection is taken by declaration identity;
    • that an empty intersection is unsellable as published.
  • The per-slice rule now describes how a buyer grades a product that breaks the rule. The "product-level list is the union" sentences now apply to products that publish placements[].

Tests

  • Schema validation 36/36, example validation 79/79, canonical conventions, fixtures (27) and negative fixtures (88/88), x-added-in-3-3-sweep 6/6.
  • mcp-schema-projection 36/36 after rebasing on main (2dc672e): the controller request measures 1_385_258 bytes, under main's 1_387_000 bound, so this PR leaves the bound alone.
  • training-agent-native-in-feed-options.test.ts + creative-agent.test.ts: 139/139.
    • The two updated training-agent assertions fail with allowed_values.
    • The new all-of test fails with the old any-of filter.
  • Server typecheck passes.
  • These also pass:
    • compliance snippet and symbol-link tests (28/28) and build:compliance --check;
    • lint-error-codes and sdk-response-conformance (14/14);
    • owned links, docs nav, schema links, and the docs JSON field audit.
  • json-schema-validation --file passes on the changed docs.
  • snippet-validation --file passes except for the 10 existing Python/JS snippets in sync_creatives.mdx, which fail locally only because the SDKs are not installed here. No code snippets were added.

Changeset

.changeset/creative-wording-errata-cta-carousel-filters.md, minor.

Out of scope / follow-ups

🤖 Generated with Claude Code

error-code
- name the closed set as cta_values on canonicals, allowed_values in
  text-asset requirements
- cover a card's per-card cta, with error.field pointing at the card
- carry the set in details.accepted_values, the core/error.json key
- error.field examples use sync_creatives request paths:
  creatives[0].assets.cta.content and creatives[0].assets.cards[2].cta

schemas and docs
- text-asset-requirements keeps MUST for the refusal and error.field,
  SHOULD for the accepted_values key; card-asset names the same code
- sync_creatives format conformance and error table say the same
- Reels table: the buyer ships assets.cta.content, not assets.cta.text
- regenerate the compliance error-code snippet

training-agent
- send details.accepted_values instead of allowed_values

Closes adcontextprotocol#8628
image_carousel
- allowed_card_media_asset_types declares default ["image"]; a
  declaration silent on it and on the deprecated alias accepts image
  cards only
- the format description says the parameter narrows image-or-video
  and defaults to image
- min_cards and max_cards only narrow the cards slot; 35 cards are
  reached by restating the slot with max: 35

responsive_creative and card-asset
- pool counts only narrow the matching slot's min and max
- card media is image only when the parameter is omitted

docs
- carousels: card media and card count section with a 35-card example
- canonical-formats, formats and sync_creatives: a parameter with a
  schema default applies it when omitted; count params never widen a
  slot

Closes adcontextprotocol#8629
schemas
- media-buy and creative list-creative-formats-request asset_types keep
  formats that declare every requested type
- an asset counts whether required or optional, top level or inside a
  repeatable_group; the example matches the rule

docs
- migration guide states the all-of rule and the one-call-per-type union
- list_creatives contrasts its any-of filter over creatives

creative-agent
- the reference list_creative_formats filter applies all-of

Closes adcontextprotocol#8630
product
- a multi-publisher product without placements[] MUST NOT carry
  publisher-scoped format_options; with no product-local declaration
  left, the seller MUST publish placements[] or split the product

docs
- formats, canonical-formats and media-products state the rule, the
  identity-based intersection and the unsellable empty case
- per-slice grading now describes a product that breaks the rule
- the product-level union applies to products that publish placements

changeset
- one adcontextprotocol patch for adcontextprotocol#8628, adcontextprotocol#8629, adcontextprotocol#8630 and adcontextprotocol#8631

Closes adcontextprotocol#8631
@mintlify

mintlify Bot commented Nov 12, 2026 •

Copy link
Copy Markdown

Preview approval required

This pull request comes from a fork, so Mintlify won't build a preview until someone with write access approves commit 1052bc1.

Approve preview

@aao-secretariat aao-secretariat Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ladon verdict: Escalate to human review

Escalate — gated schema paths require human/CODEOWNERS approval.

This PR modifies published protocol source under static/schemas/source/** (8 files, including product.json, error-code.json, image_carousel.json, responsive_creative.json, card-asset.json, and the list-creative-formats requests). These paths are under a hard, path-based approval gate (gated_paths: true), and review_decision is REVIEW_REQUIRED — not APPROVED. Per decision rule row 2, the outcome must be escalate until a human/CODEOWNERS approval lands.

The reviewer confirmed schema↔docs coherence holds across all touched surfaces and found no critical/high findings. One medium finding remains:

  • server/src/creative-agent/task-handlers.ts:224 — asset_types filter flips any-of→all-of under a patch changeset (correctly implemented and tested, but the base migration doc documented OR). This is a changeset-class judgment worth a human eye given it changes published filter semantics.

Escalation reasons

  • Touches gated protocol source (static/schemas/source/**) with review_decision: REVIEW_REQUIRED — human/CODEOWNERS approval required before merge.
  • Medium: asset_types filter semantics flip (any-of→all-of) shipped under a patch changeset — warrants confirmation the changeset class matches the wire-behavior change.

Medium findings

  • server/src/creative-agent/task-handlers.ts:224 — asset_types filter flips any-of→all-of under a patch changeset

Why human review

  • Gated paths touched (static/schemas/source/** — 8 files) and review_decision is REVIEW_REQUIRED, not APPROVED — human/CODEOWNERS approval required (decision rule row 2).
  • Medium: server/src/creative-agent/task-handlers.ts:224 — asset_types filter flips any-of→all-of under a patch changeset; changeset-class judgment on a published filter semantics change warrants a human reviewer.
  • This PR touches a path under a hard, non-overridable approval gate (static/schemas/source/core/assets/card-asset.json (modified) matches static/schemas/source/**; static/schemas/source/core/product.json (modified) matches static/schemas/source/**; static/schemas/source/core/requirements/text-asset-requirements.json (modified) matches static/schemas/source/**; static/schemas/source/creative/list-creative-formats-request.json (modified) matches static/schemas/source/**; static/schemas/source/enums/error-code.json (modified) matches static/schemas/source/**; static/schemas/source/formats/canonical/image_carousel.json (modified) matches static/schemas/source/**; static/schemas/source/formats/canonical/responsive_creative.json (modified) matches static/schemas/source/**; static/schemas/source/media-buy/list-creative-formats-request.json (modified) matches static/schemas/source/**) and the current GitHub review decision is 'REVIEW_REQUIRED', not APPROVED. This is a hard gate enforced in code — Ladon cannot auto-approve until a human/CODEOWNERS approval is recorded, regardless of how clean the diff is.

@aao-secretariat aao-secretariat Bot added the ladon/needs-human-review Ladon has escalated this PR for human review. label Nov 12, 2026
@aao-secretariat
aao-secretariat Bot requested a review from bokelley November 12, 2026 15:47
changeset
- minor, since list_creative_formats asset_types moves from any-of to all-of

@KonstantinMirin KonstantinMirin left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two notes. The first corrects a premise in my own issue.

  1. asset_types (#8630), on the class call ladon raised. My issue said nothing in the schema or in list_creative_formats.mdx marked either reading as normative. The schema part holds. The task-page part was wrong, because I only checked the 3.2 page. From 2.5 through 3.1, that page stated any-of twice: the parameter table says "Uses OR logic" (3.1.32, L41), and an "Asset Types Filter Logic" section says "formats matching ANY specified asset type are returned", with a snippet that filters the results for formats needing both (L600-L618). The 3.2 rewrite dropped both. So all-of reverses the rule the task reference documented through 3.1, not only the migration guide.

    Moving the changeset to minor keeps this out of 3.2.x, but it still changes what an unchanged request returns within 3.x, and a minor only adds. An agent built to the 3.1 page returns different formats for the same ["image", "text"] request. I'd keep any-of in 3.x, with this PR's counting of optional and repeatable_group assets, stated in the schema as plainly as this PR states all-of, and make all-of the 4.0 rule.

  2. error.field for CREATIVE_VALUE_NOT_ALLOWED. The entry's MUST says "the offending value's path within the manifest", but its examples (creatives[0].assets.cta.content) are request paths, which is what core/error.json defines field as. On create_media_buy the same value sits at packages[0].creatives[0].assets.cta.content. "the offending value's path in the request" would fit both.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ladon/needs-human-review Ladon has escalated this PR for human review.

Projects

None yet

2 participants